'''
Created on Apr 19, 2009

@author: xoon
'''

import gtk,gobject
import WindowTemplate
import Xml
import os.path

import Locations
import Shares

class XmlErrorDialog(gtk.MessageDialog):
    
    def __init__(self,message,parent=None):
        
        gtk.MessageDialog.__init__(self,parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message)
        
        self.connect("response", self.__callback)
        self.show()
        
    def __callback(self,response,data=None):
        self.destroy()


class XmlChoiceDialog(gtk.Dialog):
    
    def __init__(self,xmldatatree,message,buttons):
        
        gtk.Dialog.__init__(self,'Warning',xmldatatree,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,None)
        
        self.set_resizable(False)
        
        self.__create_content(message)
        self.__create_buttons(buttons)
        
        self.show_all()
        
    
    def __create_content(self,message):
        
        hbox = gtk.HBox()
        
        img = gtk.Image()
        img.set_from_stock(gtk.STOCK_DIALOG_WARNING,gtk.ICON_SIZE_DIALOG)
        hbox.pack_start(img)
        
        label = gtk.Label(message)
        label.set_single_line_mode(False)
        label.set_line_wrap(True)
        hbox.pack_start(label) 
        
        self.vbox.pack_start(hbox) 
        
    def __create_buttons(self,buttons):

        i = 0
        while i < len(buttons):
            self.add_button(buttons[i],i)
            i = i+1

class XmlRenameDialog(gtk.Dialog):
    
    def __init__(self,parent,old_name,existant_names):
        
        gtk.Dialog.__init__(self,'Rename %s' % (old_name),parent,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,None)
        
        self.names = existant_names
        print self.names
        
        self.__create_buttons()
        self.__create_content(old_name)
        
        self.show_all()
    
    def __create_content(self,old_name):
        
        hbox = gtk.HBox()
        
        label = gtk.Label("New name : ")
        hbox.pack_start(label,False,False,5)
                    
        self.entry = gtk.Entry()
        self.entry.connect('changed',self.__check_name)
        hbox.pack_start(self.entry,True,True,5)
        
        self.vbox.pack_start(hbox)
        
        old_name,i = old_name+'_bis',0
        while old_name in self.names:
            old_name,i = old_name+'%s'%(i),i+1
        self.entry.set_text(old_name)
    
    def __create_buttons(self):
        
        self.button_ok = gtk.Button(None,gtk.STOCK_OK,True)
        self.button_ok.set_relief(gtk.RELIEF_HALF)
        self.button_ok.connect("clicked",self.__ok)
        self.action_area.pack_start(self.button_ok,expand=True, fill=True, padding=0)
        
        button_cancel = gtk.Button(None,gtk.STOCK_CANCEL,True)
        button_cancel.set_relief(gtk.RELIEF_HALF)
        button_cancel.connect('clicked',self.__cancel)
        self.action_area.pack_start(button_cancel,expand=True, fill=True, padding=0)
    
    def __cancel(self,widget,data=None):
        self.response(gtk.RESPONSE_CANCEL)
    
    def __ok(self,widget,data=None):
        self.response(gtk.RESPONSE_OK)
        
    def __check_name(self,widget,data=None):
        text = self.entry.get_text()
        text = text.rstrip()
        if len(text) > 0 and text not in self.names:
            self.button_ok.set_sensitive(True)
        else:
            self.button_ok.set_sensitive(False)

class XmlFileChooser(gtk.FileChooserDialog):
    '''Window which allow users to select which data they want to import/export from files or the application'''

    def __init__(self,xmldatatree,title,filepath=None):
        '''Constructor'''
        
        self.xmldatatree = xmldatatree
        
        gtk.FileChooserDialog.__init__(self)
        
        self.set_select_multiple(False)
        self.set_title(title)
        self.action_type = title
        
        if self.action_type == "Import":
            self.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
        elif self.action_type == "Export":
            self.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
        
        if filepath != None:
            self.set_filename(filepath)
        
        self.__make_filters()
        self.__make_buttons(title)
        
        self.show_all()
        
        
    def __make_filters(self):
        '''Add filters to allow an easier selection:
    -"XML Files" to list only .xml files
    -"All files" to list every file'''
    
        self.filter = gtk.FileFilter()
        self.filter.set_name("XML Files")
        self.filter.add_pattern("*.xml")
        self.add_filter(self.filter)
        
        self.filter = gtk.FileFilter()
        self.filter.set_name("All files")
        self.filter.add_pattern("*")
        self.add_filter(self.filter)
    
    def __make_buttons(self,title):
        '''Add cancel and ok buttons to the window'''
        
        self.button_cancel = gtk.Button("Cancel",gtk.STOCK_CANCEL,True)
        self.button_cancel.set_relief(gtk.RELIEF_NORMAL)
        self.button_cancel.connect("clicked",self.__destroy,"cancel")
        self.action_area.pack_start(self.button_cancel,expand=True, fill=True, padding=0)
        
        self.button_ok = gtk.Button(None,gtk.STOCK_OK,True)
        self.button_ok.set_relief(gtk.RELIEF_HALF)
        self.button_ok.connect("clicked",self.__action,title)
        self.action_area.pack_start(self.button_ok,expand=True, fill=True, padding=0)
     
    def __action(self, widget, data=None):
        '''Fonction called when user clicked to import or export xml data'''
        
        if self.get_filename() != None and not os.path.isdir(self.get_filename()):
    
            if self.action_type == "Import":
                xml_file = Xml.Xml(self.get_filename())
                result = xml_file.validate()
                if result != None:
                    self.message = XmlErrorDialog(gtk.MESSAGE_ERROR, 'Failed to validate the file against the dtd',self)
                else:
                    self.xmldatatree.load_data(self.get_filename())
                    self.destroy()
            elif self.action_type == "Export":
                self.xmldatatree.chooser_view.set_text(self.get_filename())
                self.destroy()          
        #else:
        #    self.set_current_folder(self.get_filename())   
    
    
    def __destroy(self, widget, data=None):
        '''Fonction called when user clicked the cancel button'''

        self.destroy() 

class XmlDataTree(WindowTemplate.WindowTemplate):
    
    def __init__(self, controller, type):
        '''constructor:
    controller : instance of Controller.py
    type : "Export" or "Import"'''
        
        self.action_type = type
              
        WindowTemplate.WindowTemplate.__init__(self,controller)
        
        self.__create_window()
        
        if self.action_type == "Export":
            self.__load_from_application()
        
        self.show_all()
    
    def __create_window(self):
        '''Create general properties of the window'''
        
        self.set_default_size(400,500)
        self.vbox = gtk.VBox(False,10)
        self.add(self.vbox)
        
        self.__create_chooser()
        self.__create_tree()
        self.__create_buttons(self.action_type)
    
    def __create_tree(self): 
        '''Create the treestore and his treeview'''
        
        self.treestore = gtk.TreeStore(gobject.TYPE_BOOLEAN,gobject.TYPE_STRING)                
        
        self.treeview = gtk.TreeView(self.treestore)
        self.column = gtk.TreeViewColumn("Tree")
        self.treeview.append_column(self.column)
        
        self.celltoggle = gtk.CellRendererToggle()
        self.celltoggle.connect('toggled', self.__toggle)
        self.column.pack_start(self.celltoggle,False)
        self.column.add_attribute(self.celltoggle,'active',0)
        
        self.celltext = gtk.CellRendererText()
        self.column.pack_start(self.celltext,False)
        self.column.add_attribute(self.celltext,'text',1)
        
        self.treeview.set_border_width(0)
        
        self.scrolled_window = gtk.ScrolledWindow()
        self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_ALWAYS)
        self.scrolled_window.add_with_viewport(self.treeview)
        
        self.vbox.pack_start(self.scrolled_window,expand=True, fill=True, padding=0)           
    
    def __create_buttons(self,title):
        '''Add expands, cancel and ok buttons to the window'''
        
        self.hbox_buttons = gtk.HBox()
        self.hbox_buttons.set_border_width(5)
        
        self.button_expand_all = gtk.Button("_Expand all",None,True)
        self.button_expand_all.set_relief(gtk.RELIEF_HALF)
        self.button_expand_all.connect("clicked",self.__expand,"expand")
        self.hbox_buttons.pack_start(self.button_expand_all,expand=False, fill=False, padding=5)
        
        self.button_hide_all = gtk.Button("_Hide all",None,True)
        self.button_hide_all.set_relief(gtk.RELIEF_HALF)
        self.button_hide_all.connect("clicked",self.__expand,"hide")
        self.hbox_buttons.pack_start(self.button_hide_all,expand=False, fill=False, padding=5)
        
        self.button_ok = gtk.Button(None,gtk.STOCK_OK,True)
        self.button_ok.set_relief(gtk.RELIEF_HALF)
        self.button_ok.connect("clicked",self.__action,title)
        self.hbox_buttons.pack_end(self.button_ok,expand=False, fill=False, padding=5)
        
        self.button_cancel = gtk.Button("Cancel",gtk.STOCK_CANCEL,True)
        self.button_cancel.set_relief(gtk.RELIEF_NORMAL)
        self.button_cancel.connect("clicked",self.__destroy,"cancel")
        self.hbox_buttons.pack_end(self.button_cancel,expand=False, fill=False, padding=5)
        
        self.vbox.pack_end(self.hbox_buttons,False,False,5)
    
    def __create_chooser(self):
        '''Add a container to the window which allow user to browse the filesystem'''
        
        self.hbox_chooser = gtk.HBox()
        self.hbox_chooser.set_border_width(5)

        self.chooser_label = gtk.Label("File : ")
        self.hbox_chooser.pack_start(self.chooser_label,False,False,5)
                    
        self.chooser_view = gtk.Entry()
        self.hbox_chooser.pack_start(self.chooser_view,True,True,5)
        
        self.button_browse = gtk.Button(None,gtk.STOCK_OPEN,True)
        self.button_browse.set_relief(gtk.RELIEF_HALF)
        self.button_browse.connect("clicked",self.__browse,None)
        self.hbox_chooser.pack_start(self.button_browse,False,False,5)
        
        self.vbox.pack_start(self.hbox_chooser,False,True,5)
    
    def __make_tree(self,data):
        '''Clear and fill the treestore with data given in argument'''
        
        self.treestore.clear()
        
        self.data = data        
        listdico,listtree = list(),list()
        
        listdico.append(self.data)
        listtree.append(None)
        
        while len(listdico) > 0:
            dico,tree = listdico.pop(0),listtree.pop(0)

            if dico.has_key(0) and dico[0].has_key('type') and dico[0]['type'] == "name" :
                #child = self.treestore.append(tree,['%s : %s' % (dico['type'],dico[0]['value'])])
                child = self.treestore.append(tree,[True,dico[0]['value']])
            elif dico['type'] != 'name' and dico.has_key('value'):
                child = self.treestore.append(tree,[True,"%s : %s" % (dico['type'],dico['value'])])
                continue
            elif dico.has_key('value') :
                continue
            else:
                child = self.treestore.append(tree,[True,dico['type']])
            
            for key in dico:
                if key not in ['type','check','value']:
                    listdico.append(dico[key])
                    listtree.append(child)
                    
        self.treeview.expand_row("0",False)
        
        
    def __browse(self,widget,data = None):
        '''Create a filechooserdialog to look for files with a xml filter'''
        
        path =  self.chooser_view.get_text()
        if len(path) > 0 :
            XmlFileChooser(self,self.action_type,path)
        else:
            XmlFileChooser(self,self.action_type,None)
    
    def __expand(self, widget, data=None):
        '''fully expand or collapse the treeview whether data contains "expand" or "hide"'''
        
        if data == "expand":
            self.treeview.expand_all()
        elif data == "hide":
            self.treeview.collapse_all()
        else:
            None
    
    def __toggle(self,widget,viewpath):
        '''Select/Unselect rows of the treeview with a chain effect over the tree'''
                       
        path = tuple([int(i) for i in viewpath.split(':')])
        
        self.treestore[path][0] = not self.treestore[path][0]
        
        self.__chain_down_tree(self.treestore[path],self.treestore[path][0])
        
        if self.treestore[path][0]:
            self.__chain_up_tree(self.treestore[path], True)           
            
    def __chain_up_tree(self,treerow,value):
        '''Select/Unselect parents of the treerow to match the value given in argument'''
        
        child = treerow
        while(child != None and child.parent != None):
            parent = child.parent
            if child[0] == value and parent[0] != value:
                parent[0] = value
                child = parent
            else:
                child = None
    
    def __chain_down_tree(self,treerow,value):
        '''Select/Unselect childs of the treerow to match the value given in argument'''
        
        listiter = list()
        listiter.append(treerow)
        
        while len(listiter) > 0:
            parent = listiter.pop()
            for child in parent.iterchildren():
                child[0] = value
                if child.iterchildren() != None:
                    listiter.append(child)
    

    def __action(self, widget, data=None):
        '''Fonction called when user clicked to import or export xml data'''
        
        check = True
        error_msg = None
        
        if self.action_type == 'Export':
            filename = self.chooser_view.get_text()
            filename = filename.strip()
            print len(filename)
            if len(filename) == 0:
                check = False
                error_msg = 'You must enter a valid filepath'
            elif os.path.exists(os.path.dirname(filename)) == False:
                check = False
                error_msg = 'The directory ' + os.path.dirname(filename) + ' does not exist'
            elif len(os.path.basename(filename).strip()) == 0:
                check = False
                error_msg = 'You must enter a correct filename'

        elif self.action_type == 'Import' and len(self.treestore) == 0:
            check = False
            error_msg = 'There is nothing to import'
            
        
        if check is True:            
            dico,pile = dict(),list()
            pile.append(self.treestore[0])
            
            while len(pile) > 0:
                elem = pile.pop(0)
                if elem[0] is True:
                    path = list(elem.path)
                    path.remove(0)
                    self.__add_to_dico(dico,path)
                    for child in elem.iterchildren():
                        pile.append(child)
                        
            if len(dico.keys()) > 0:
                if self.action_type == 'Import':
                    self.__load_into_application(dico)
                elif self.action_type == 'Export':
                    self.__load_into_file(dico)
        else:
            message = XmlErrorDialog(error_msg,self)
    
    def __load_into_file(self,dico):
        '''Export content of dico in the file of the chooser'''

        write = True
        filename = self.chooser_view.get_text()
        if os.path.exists(filename) and not os.path.isdir(filename):
            message = 'The file %s already exists. Overwrite ?' % (filename)
            buttons = ['Cancel','Ok']
            dialog = XmlChoiceDialog(self,message,buttons)
            response = dialog.run()
            dialog.destroy()
            
            if response is None or response != 1:                        
                write = False
            
        if write is True:
            xml = Xml.Xml(filename)
            write = xml.write_data(dico)
            if write is not None:
                error = XmlErrorDialog('Error occured : %s' % (write),self)
            else:
                self.destroy()
    
    def __load_into_application(self,dico):
        '''Import the data in dico in the application'''

        d = dict(dico)
        clean = self.__check_for_occurences(d)
        
        if clean is True:
            dico = d
            
            dico_location = {}
            for i in self.locations.store:
                loc = self.locations.store[i]
                dico_location[loc.name] = loc.id
            
            for dkey in dico.iterkeys():
                dd = dico[dkey]
                if dd['type'] == 'location':
                    
                    name = dd[0]['value']
                    if dico_location.has_key(dd[0]['value']):
                        loc_id = dico_location[name]
                        self.locations.__delitem__(loc_id)
                    else :
                        loc_id = self.locations.get_id()
                    loc = Locations.Location(loc_id)
                    
                    shares = None
                    for ddkey in dd:
                        shares = None
                        type = None
                        if ddkey == 'type':
                            continue
                        elif dd[ddkey]['type'] == 'public_addr':
                            type = 'public'
                        elif dd[ddkey]['type'] == 'local_addr':
                            type = 'local'
                        elif dd[ddkey]['type'] == 'dns':
                            type = 'dns'
                        elif dd[ddkey]['type'] == 'name':
                            loc.name = name
                        elif dd[ddkey]['type'] == 'shares':
                            shares = dict()
                            i = 0
                            for skey in dd[ddkey].iterkeys():
                                if skey != 'type':
                                    shares[i] = dict()
                                    for vkey in dd[ddkey][skey].iterkeys():
                                        if vkey == 'type':
                                            None
                                        elif dd[ddkey][skey][vkey]['value']!=None and dd[ddkey][skey][vkey]['value'].strip() != "":
                                            shares[i][dd[ddkey][skey][vkey]['type']] = dd[ddkey][skey][vkey]['value']
                                    if len(shares[i]) != 0:
                                        i = i+1
                                    else :
                                        del shares[i]
                                        
                            if len(shares) == 0:
                                shares = None
                         
                        print shares
                                            
                        if type != None:
                            for vkey in dd[ddkey].iterkeys():
                                if vkey != 'type' and dd[ddkey][vkey]['value'] != None:
                                    loc.add_param(type, dd[ddkey][vkey]['value'])
                        
                        if shares != None:
                            for skey in shares.iterkeys():
                                sshare = shares[skey]
                                share = None
                                share = Shares.Share(self.shares.get_id())
                                share.location = loc
                                for vkey in sshare.iterkeys():
                                    if vkey == 'name':
                                        share.name = sshare[vkey]
                                    elif vkey == 'server':
                                        share.server = sshare[vkey] 
                                    elif vkey == 'mount_point':
                                        share.mount_point = sshare[vkey] 
                                    elif vkey == 'path':
                                        share.path = sshare[vkey]
                                    elif vkey == 'domain':
                                        share.domain = sshare[vkey]
                                    elif vkey == 'login':
                                        share.login = sshare[vkey]
                                    elif vkey == 'protocol':
                                        share.protocol = sshare[vkey]
                                self.shares[share.id] = share    
                        self.locations[loc.id] = loc
               
            self.destroy()
                        
    def __load_from_application(self):
        
        dico,corresp = dict(),dict()
        dico['type'] = 'MIAM'
        
        h = 0    
        for  i in self.locations.store:
            loc = self.locations[i]
            corresp[i] = h
            dico[h]  = dict()
            dico[h]['type'] = 'location'
            
            j = 0
            dico[h][j] = dict()
            dico[h][j]['type'] ='name'
            dico[h][j]['value'] = loc.name
            
            j = j+1
            pile= [[loc.public_addr,'public_addr','ip'],
                   [loc.local_addr,'local_addr','ip'],
                   [loc.dns,'dns','domain']]
            
            while len(pile) > 0:
                elem = pile.pop(0)
                if len(elem[0]) > 0:
                    dico[h][j] = dict()
                    dico[h][j]['type'] = elem[1]
                    k = 0
                    for val in elem[0]:
                        dico[h][j][k] = dict()
                        dico[h][j][k]['type'] = elem[2]
                        dico[h][j][k]['value'] = val
                        k = k+1
                    j = j+1
            h = h+1
        
        for i in self.shares.store:
            sh = self.shares[i]
            loc = dico[corresp[sh.location.id]]
            j = len(loc)-2
            print sh.name
            if loc[j]['type'] != 'shares':
                j = j+1
                loc[j] = dict()
                loc[j]['type'] = 'shares'
            shs = loc[j]
            h = len(shs)-1
            shs[h] = dict()
            shs[h]['type'] = 'share'
            k = 0
            pile = [['name',sh.name],
                    ['protocol',sh.protocol],
                    ['server',sh.server],
                    ['path',sh.path],
                    ['domain',sh.domain],
                    ['mount_point',sh.mount_point],
                    ['login',sh.login]]
            
            while len(pile) > 0:
                elem = pile.pop(0)
                if elem[1] != None:
                    shs[h][k] = dict()
                    shs[h][k]['type'] = elem[0]
                    shs[h][k]['value'] = elem[1]
                    k = k+1
            
        self.__make_tree(dico)        
        
    def __add_to_dico(self,dico,path):
        '''Add data from dico[path] with path given in argument'''
        
        d = self.data
        if len(path)>0:
            k = path[len(path)-1]
            for key in path:
                if d[0]['type'] == 'name':
                    key = key+1
                d = d[key]
                
                if not dico.has_key(key):
                    dico[key] = dict()
                dico = dico[key]
                
                if not d.has_key('value') and d[0]['type'] == 'name':
                    dico[0] = dict()
                    dico[0]['type'] = d[0]['type']
                    dico[0]['value'] = d[0]['value']
                    
            dico['type'] = d['type']
            if d.has_key('value'): 
                dico['value'] = d['value']
    
    def __check_for_occurences(self,dico):
        
        dic = {}
        
        for key in dico.iterkeys(): 
            name = dico[key][0]['value']
            if dico[key]['type'] == 'location':
                dic[name] = {}
                dic[name]['dic'] = key
        
        for i in self.locations.store:
            name = self.locations.store[i].name
            if not dic.has_key(name):
                dic[name] = {}
            dic[name]['loc'] = True
        
        
        # Cancel must be first
        options = ['Cancel','Rename','Replace','Replace all','Skip']
        
        problems = True
        while problems:
            problems = False
            key_list = dic.keys()
            while len(key_list) > 0:
                key = key_list.pop(0)
                if dic[key].has_key('loc') and dic[key].has_key('dic'):
                    response = None
                    message = 'The location %s already exists in the application' % (key)
                    dialog = XmlChoiceDialog(self,message,options)
                    response = dialog.run()
                    dialog.destroy()
                    
                    if response is None or response <= 0:
                        return False
                    else:
                        if response > 0 and response < len(options):
                            choix = options[response]
                            if choix == 'Skip':
                                del dico[dic[key]['dic']]
                                del dic[key]['dic']
                            elif choix == 'Rename':
                                dialog = XmlRenameDialog(self,key,dic.keys())
                                response = dialog.run()
                                 
                                if response is not None and response == gtk.RESPONSE_OK:
                                    new_name = dialog.entry.get_text().rstrip()
                                    dico[dic[key]['dic']][0]['value'] = new_name
                                    dic[new_name] = {}
                                    dic[new_name]['dic'] = {}
                                    dic[new_name]['dic'] = dico[dic[key]['dic']]
                                    key_list.append(new_name)
                                    del dic[key]['dic']
                                else:
                                    del dico[dic[key]['dic']]
                                    del dic[key]['dic']
                                dialog.destroy()
                            elif choix == 'Replace all':
                                break
                            elif choix == 'Replace':
                                del dic[key]['loc']
        return True                        
                    
                        

    
    def __destroy(self, widget, data=None):
        '''Fonction called when user clicked the cancel button'''
        self.destroy()
        
    def load_data(self,filename):
        '''Load data from filename if the file is valid '''
        
        self.chooser_view.set_text(filename)

        if self.action_type == "Import":
            if(filename != None):
                xml_file = Xml.Xml(filename)
                result = xml_file.validate()
                if result is None:
                    self.__make_tree(xml_file.get_data())
                else:
                    self.message = XmlErrorDialog(gtk.MESSAGE_ERROR, 'Failed to validate the file against the dtd',self)
    
